/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年5月30日
 */
package com.massyframework.beanskin.runtime.framework.installing;

import java.util.HashSet;
import java.util.Set;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.util.localstore.LocalStore;
import com.massyframework.beanskin.util.variables.MapVariableReplacer;
import com.massyframework.beanskin.util.variables.StringVariableReplacer;
import com.massyframework.beanskin.util.version.Version;

/**
 * 模块标识符解析
 * @author huangkh
 *
 */
public abstract class ModuleIdentifierModuleParser<B> extends AbstractModuleParser<B> {
	
	protected static final String NAME         = "name";
	protected static final String DESCRIPTION  = "description";
	protected static final String TAGS         = "tags";
	
	protected static final String DEVELOPER    = "developer";
	protected static final String SHORTNAME    = "short-name";
	protected static final String HOMEPAGE     = "home-page";
	
	protected static final String GROUPID      = "groupId";
	protected static final String MODULEID     = "moduleId";
	protected static final String VERSION      = "version";
			
	/**
	 * 构造方法
	 * @param kernel {@link Assembly},内核的配置参数
	 * @param localStore {@link LocalStore},本地存储
	 */
	public ModuleIdentifierModuleParser(Assembly kernel, LocalStore localStore) {
		super(kernel, localStore);
	}

	@Override
	protected final void doParse(Document doc, B builder) {
		MapVariableReplacer replacer = new MapVariableReplacer();
		Expression<String> expression = this.createExpression(builder, replacer);
		
		
		NodeList nodes = doc.getDocumentElement().getChildNodes();
		int size = nodes.getLength();
		int time = 0;
		
		SimpleModuleIdentifier.Builder miBuilder = SimpleModuleIdentifier.newBuilder();
		for (int i=0; i<size; i++) {
			Node node = nodes.item(i);
			String name = node.getNodeName();
			if (name.equals(GROUPID)) {
				miBuilder.groupId(this.getTextContent(node));
				time +=1;
				if (time == 3) {
					this.checkModuleIdentifier(miBuilder, this.getModuleIdentifier(), builder);
				}
				continue;
			}
			if (name.equals(MODULEID)) {
				miBuilder.moduleId(this.getTextContent(node));
				time +=1;
				if (time == 3) {
					this.checkModuleIdentifier(miBuilder, this.getModuleIdentifier(), builder);
				}
				continue;
			}
			if (name.equals(VERSION)) {
				miBuilder.version(Version.parseVersion(this.getTextContent(node)));
				time +=1;
				if (time == 3) {
					this.checkModuleIdentifier(miBuilder, this.getModuleIdentifier(), builder);
				}
				continue;
			}
			
			if (name.equals(NAME)) {
				this.setModuleName(this.getTextContent(node), builder);
				continue;
			}
			
			if (name.equals(DESCRIPTION)) {
				this.setModuleDescription(this.getTextContent(node), builder);
				continue;
			}
			
			if (name.equals(TAGS)) {
				String text = this.getTextContent(node);
				String[] arr = text.split(",");
				Set<String> tags = new HashSet<String>();
				for (String tag: arr) {
					tag = tag.trim();
					if (tag.length() > 0) {
						tags.add(tag);
					}
				}
				this.setTags(tags, builder);
				continue;
			}
			
			if (name.equals(DEVELOPER)) {
				this.doParserDeveloper(node.getChildNodes(), builder);
				continue;
			}
						
			this.doParseNode(node, expression, replacer, builder);
		}
		
		this.doParseNodeAfterInitParams(doc, expression, replacer, builder);
	}
	
	protected void doParserDeveloper(NodeList nodes, B builder) {
		int size = nodes.getLength();
		String shortName = null;
		String name = null;
		String homePage = null;
		
		for (int i=0; i<size; i++) {
			Node node = nodes.item(i);
			String nodeName = node.getNodeName();
			
			
			if (nodeName.equals(SHORTNAME)) {
				shortName = this.getTextContent(node);
				continue;
			}
			
			if (nodeName.equals(NAME)) {
				name = this.getTextContent(node);
				continue;
			}
			
			if (nodeName.equals(HOMEPAGE)) {
				homePage = this.getTextContent(node);
				continue;
			}
		}
		
		if (shortName != null) {
			this.setDeveloper(shortName, name, homePage, builder);
		}
	}
	
	/**
	 * 检查模块标识符与存放目录是否一致
	 * @param miBuilder {@link SimpleModuleIdentifier.Builder}, 模块构建器
	 * @param identifier {@link ModuleIdentifier}, 模块标识符
	 * @param builder {@link B}, 构建器
	 */
	protected void checkModuleIdentifier(SimpleModuleIdentifier.Builder miBuilder, ModuleIdentifier identifier, B builder) {
		ModuleIdentifier other = miBuilder.build();
		if (!other.equals(identifier)) {
			throw new RuntimeException("配置文件中的模块定义和目录不一致：" + identifier.toString() + ".");
		}else {
			this.setModuleIdentifier(identifier, builder);
		}
	}
	
	/**
	 * 解析节点
	 * @param node {@link Node},待解析的节点
	 * @param expression {@link Expression}，表达式
	 * @param replacer {@link StringVariableReplacer}, 字符串变量替换器
	 * @param builder {@link B}
	 */
	protected abstract void doParseNode(Node node, Expression<String> expression, 
			StringVariableReplacer replacer, B builder);	
	
	/**
	 * 在解析初始化参数后，解析后续节点
	 * <p>
	 * 初始化参数需要在某些节点前解析，才能在后续节点解析中利用初始化参数的表达式进行某些计算。
	 * @param doc {@link Document}
	 * @param expression {@link Expression},表达式
	 * @param replaver {@link StringVariableReplacer}, 字符变量替换器
	 * @param builder {@link B}
	 */
	protected void doParseNodeAfterInitParams(Document doc, Expression<String> expression, 
			StringVariableReplacer replacer, B builder) {
	}
	
	/**
	 * 设置模块标识符
	 * @param identifier {@link ModuleIdentifier}
	 * @param builder {@link B}
	 */
	protected abstract void setModuleIdentifier(ModuleIdentifier identifier, B builder);
	
	/**
	 * 设置模块名称
	 * @param builder {@link B}
	 * @param moduleName {@link String}
	 */
	protected abstract void setModuleName(String moduleName, B builder);
	
	/**
	 * 设置模块描述
	 * @param description {@link String}
	 * @param builder {@link B}
	 */
	protected abstract void setModuleDescription(String description, B builder);
	
	/**
	 * 设置标签集
	 * @param tags {@link Set}
	 * @param builder {@link B}
	 */
	protected abstract void setTags(Set<String> tags, B builder);
	
	/**
	 * 设置开发商
	 * @param shortName {@link String},简称
	 * @param name {@link String},全称，可能为null
	 * @param homePage {@link String},主页， 可能为null
	 * @param builder {@link B}
	 */
	protected abstract void setDeveloper(String shortName, String name, String homePage, B builder);
}
